package com.example.testng.utils;

import com.example.testng.model.TestSuite;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 用于加载测试数据的工具类。
 */
public class TestDataLoader {
    private static final ObjectMapper jsonMapper = new ObjectMapper()
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    private static final ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory())
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

    // 线程池配置
    private static final int THREAD_POOL_SIZE = Runtime.getRuntime().availableProcessors();
    private static final ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);

    /**
     * 从JSON文件加载测试套件
     *
     * @param path JSON文件路径
     * @return 测试套件对象
     * @throws IOException 如果文件读取失败
     */
    public static TestSuite loadFromJson(String path) throws IOException {
        return jsonMapper.readValue(new File(path), TestSuite.class);
    }

    /**
     * 关闭线程池资源
     * 应在应用程序关闭时调用此方法
     */
    public static void shutdown() {
        executor.shutdown();
        try {
            if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
            Thread.currentThread().interrupt();
        }
    }

    /**
     * 从YAML文件加载测试套件
     *
     * @param path YAML文件路径
     * @return 测试套件对象
     * @throws IOException 如果文件读取失败
     */
    public static TestSuite loadFromYaml(String path) throws IOException {
        try {
            return yamlMapper.readValue(new File(path), TestSuite.class);
        } catch (IOException e) {
            System.err.println("Warning: Failed to load test suite from " + path + ": " + e.getMessage());
            throw e;
        }
    }

    /**
     * 从指定模式的YAML文件加载多个测试套件
     *
     * @param pattern YAML文件路径模式
     * @return 测试套件对象列表
     * @throws IOException 如果文件读取失败
     */
    /**
     * 加载指定的YAML文件
     *
     * @param filePattern 文件名或文件路径模式
     * @return 测试套件对象列表
     * @throws IOException 如果文件读取失败
     */
    public static List<TestSuite> loadSpecificYamlFile(String filePattern) throws IOException {
        long startTime = System.currentTimeMillis();
        List<TestSuite> suites = new ArrayList<>();
        Path basePath = Paths.get(System.getProperty("user.dir"));
        System.out.println("Base path: " + basePath);

        // 处理文件模式 - 创建final变量
        final String normalizedPattern = !filePattern.startsWith("src/main/resources/") ?
                "src/main/resources/" + filePattern : filePattern;

        System.out.println("Looking for specific file(s): " + normalizedPattern);

        // 使用Files.walk来查找匹配的文件
        List<Path> yamlFiles = new ArrayList<>();
        try {
            // 创建final变量用于lambda表达式
            final String patternForLambda = normalizedPattern;

            yamlFiles = Files.walk(basePath)
                    .filter(path -> path.toString().toLowerCase().endsWith(".yml"))
                    .filter(path -> {
                        String relativePath = basePath.relativize(path).toString().replace("\\", "/");
                        // 检查文件名是否匹配
                        String fileName = path.getFileName().toString();
                        boolean matches = false;

                        // 如果是通配符模式，使用路径匹配
                        if (patternForLambda.contains("*")) {
                            matches = relativePath.contains("testcases") &&
                                    relativePath.matches(patternForLambda.replace("**", ".*").replace("*", "[^/]*"));
                        }
                        // 否则，直接检查文件名是否匹配
                        else {
                            String targetFileName = new File(patternForLambda).getName();
                            matches = relativePath.contains("testcases") && fileName.equals(targetFileName);
                        }

                        if (matches) {
                            System.out.println("Found matching file: " + relativePath);
                        }
                        return matches;
                    })
                    .distinct()
                    .collect(Collectors.toList());
        } catch (IOException e) {
            System.err.println("Error walking file tree: " + e.getMessage());
        }

        if (yamlFiles.isEmpty()) {
            System.out.println("No specific test suite files found matching: " + filePattern);
            throw new IOException("No valid test suites found for: " + filePattern);
        }

        System.out.println("Found " + yamlFiles.size() + " matching YAML files:");
        yamlFiles.forEach(path -> System.out.println(" - " + path));

        // 使用CompletableFuture并行加载YAML文件
        for (Path yamlFile : yamlFiles) {
            try {
                TestSuite suite = loadFromYaml(yamlFile.toString());
                System.out.println("Successfully loaded test suite from: " + yamlFile);
                suites.add(suite);
            } catch (IOException e) {
                System.err.println("Warning: Failed to load test suite from " + yamlFile + ": " + e.getMessage());
            }
        }

        if (suites.isEmpty()) {
            throw new IOException("No valid test suites could be loaded from: " + filePattern);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("加载 " + suites.size() + " 个测试套件耗时: " + (endTime - startTime) + " ms");

        return suites;
    }

    /**
     * 从指定模式的YAML文件加载多个测试套件
     *
     * @param pattern YAML文件路径模式
     * @return 测试套件对象列表
     * @throws IOException 如果文件读取失败
     */
    public static List<TestSuite> loadFromYamlPattern(String pattern) throws IOException {
        long startTime = System.currentTimeMillis();
        List<TestSuite> suites = new ArrayList<>();
        Path basePath = Paths.get(System.getProperty("user.dir"));
        System.out.println("Base path: " + basePath);
        System.out.println("使用 " + THREAD_POOL_SIZE + " 个线程进行并行解析");

        // 简化路径处理逻辑
        String normalizedPattern = pattern;
        if (pattern.startsWith("src/main/resources/")) {
            // 如果路径以src/main/resources/开头，尝试直接使用相对路径
            normalizedPattern = pattern;
        } else {
            // 否则，尝试在classpath中查找
            normalizedPattern = "src/main/resources/" + pattern;
        }

        System.out.println("Looking for files matching pattern: " + normalizedPattern);

        // 使用Files.walk来遍历目录
        List<Path> yamlFiles = new ArrayList<>();
        try {
            yamlFiles = Files.walk(basePath)
                    .filter(path -> path.toString().toLowerCase().endsWith(".yml"))
                    .filter(path -> {
                        String relativePath = basePath.relativize(path).toString().replace("\\", "/");
                        boolean matches = relativePath.contains("testcases");
                        if (matches) {
                            System.out.println("Found potential match: " + relativePath);
                        }
                        return matches;
                    })
                    .distinct() // 确保没有重复的路径
                    .collect(Collectors.toList());
        } catch (IOException e) {
            System.err.println("Error walking file tree: " + e.getMessage());
        }

        if (yamlFiles.isEmpty()) {
            System.out.println("No test suite files found matching pattern: " + pattern);
            throw new IOException("No valid test suites found in pattern: " + pattern);
        }

        System.out.println("Found " + yamlFiles.size() + " YAML files:");
        yamlFiles.forEach(path -> System.out.println(" - " + path));

        // 使用CompletableFuture并行加载YAML文件
        java.util.Set<String> loadedPaths = java.util.Collections.synchronizedSet(new java.util.HashSet<>());
        List<CompletableFuture<TestSuite>> futures = new ArrayList<>();

        for (Path yamlFile : yamlFiles) {
            String filePath = yamlFile.toString();
            if (loadedPaths.contains(filePath)) {
                System.out.println("Skipping already loaded file: " + filePath);
                continue;
            }

            loadedPaths.add(filePath);

            // 创建异步任务
            CompletableFuture<TestSuite> future = CompletableFuture.supplyAsync(() -> {
                try {
                    TestSuite suite = loadFromYaml(filePath);
                    System.out.println("Successfully loaded test suite from: " + yamlFile);
                    return suite;
                } catch (IOException e) {
                    System.err.println("Warning: Failed to load test suite from " + yamlFile + ": " + e.getMessage());
                    return null;
                }
            }, executor);

            futures.add(future);
        }

        // 等待所有异步任务完成并收集结果
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

        // 收集结果
        for (CompletableFuture<TestSuite> future : futures) {
            try {
                TestSuite suite = future.get();
                if (suite != null) {
                    suites.add(suite);
                }
            } catch (Exception e) {
                System.err.println("Error getting result from future: " + e.getMessage());
            }
        }

        if (suites.isEmpty()) {
            throw new IOException("No valid test suites could be loaded from pattern: " + pattern);
        }

        long endTime = System.currentTimeMillis();
        System.out.println("并行加载 " + suites.size() + " 个测试套件耗时: " + (endTime - startTime) + " ms");

        return suites;
    }
}